// // #include <stdio.h>
// // #include <stdint.h>
// // #include <bsl_asn1.h>

// // int func(int line, int depth, int hl, int length, const char *s, int tag){
// //     printf("%4d:d=%1d  hl=%1d l=%4d %s",line, depth, hl, length, s);

// //     for(int i=0; i<depth; i++) printf(" ");

// //     printf("SET\n");

// //     switch (tag)
// //     {
// //     case constant expression:
// //         /* code */
// //         break;
    
// //     default:
// //         break;
// //     }


// // }
// // /*
// // typedef struct _BSL_ASN1_Buffer {
// //     uint8_t tag;
// //     uint32_t len;
// //     uint8_t *buff;
// // } BSL_ASN1_Buffer;
// // */


// // int32_t Asn1ParseFormat(BSL_UIO *outUio, BSL_ASN1_Buffer *buff,
// //                         uint32_t dlimit, uint32_t indent, uint32_t dump);

// // int32_t Asn1ParseFormat(BSL_UIO *outUio, BSL_ASN1_Buffer *buff,
// //                         uint32_t dlimit, uint32_t indent, uint32_t dump){
    
// //     const uint8_t tag = buff->tag;


// //     printf("%4d:d=%1d  hl=%1d l=%4d %s",line, depth, hl, length, s);

// //     switch (tag) {

// //     case BSL_ASN1_TAG_UTF8STRING:
// //         printf("UTF8STRING\n");
// //         break;
// //     case BSL_ASN1_TAG_RALATIVE_ID:
// //         printf("RALATIVE_ID\n");
// //         break;
// //     case BSL_ASN1_TAG_TIME:
// //         printf("TIME\n");
// //         break;
// //     case BSL_ASN1_TAG_SEQUENCE:
// //         printf("SEQUENCE\n");
// //         break;
// //     case BSL_ASN1_TAG_SET:
// //         printf("SET\n");
// //         break;
// //     case BSL_ASN1_TAG_PRINTABLESTRING:
// //         printf("PRINTABLESTRING\n");
// //         break;
// //     case BSL_ASN1_TAG_T61STRING:
// //         printf("T61STRING\n");
// //         break;
// //     case BSL_ASN1_TAG_IA5STRING:
// //         printf("IA5STRING\n");
// //         break;
// //     case BSL_ASN1_TAG_UTCTIME:
// //         printf("UTCTIME\n");
// //         break;
// //     case BSL_ASN1_TAG_GENERALIZEDTIME:
// //         printf("GENERALIZEDTIME\n");
// //         break;
// //     case BSL_ASN1_TAG_BMPSTRING:
// //         printf("BMPSTRING\n");
// //         break;
        
// //     default:
// //         break;
// //     }                   
                     
// // }

// // int main(){
// //     int line, depth, hl, length;
// //     line = 83; depth = 3; hl = 2; length = 16;
// //     char *s = "cons: ";

// //     func(line, depth, hl, length, s);

// //     return 0;

// // }
// // /*
// //    83:d=3  hl=2 l=  16 cons:    SET               
// //    85:d=4  hl=2 l=  14 cons:     SEQUENCE          
// //    87:d=5  hl=2 l=   3 prim:      OBJECT            :localityName
// //    92:d=5  hl=2 l=   7 prim:      UTF8STRING        :Beijing
// //   101:d=3  hl=2 l=  13 cons:
// // */


// static int asn1_print_fsname(BIO *out, int indent,
//                              const char *fname, const char *sname,
//                              const ASN1_PCTX *pctx)
// {
//     static const char spaces[] = "                    ";
//     static const int nspaces = sizeof(spaces) - 1;


//     while (indent > nspaces) {
//         if (BIO_write(out, spaces, nspaces) != nspaces)
//             return 0;
//         indent -= nspaces;
//     }
//     if (BIO_write(out, spaces, indent) != indent)
//         return 0;

//     if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
//         sname = NULL;
//     if (pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
//         fname = NULL;
//     if (!sname && !fname)
//         return 1;
//     if (fname) {
//         if (BIO_puts(out, fname) <= 0)
//             return 0;
//     }

//     if (sname) {
//         if (fname) {
//             if (BIO_printf(out, " (%s)", sname) <= 0)
//                 return 0;
//         } else {
//             if (BIO_puts(out, sname) <= 0)
//                 return 0;
//         }
//     }
//     if (BIO_write(out, ": ", 2) != 2)
//         return 0;
//     return 1;
// }

// static int asn1_print_info(BIO *bp, long offset, int depth, int hl, long len,
//                            int tag, int xclass, int constructed, int indent)
// {
//     char str[128];
//     const char *p;
//     int pop_f_prefix = 0;
//     long saved_indent = -1;
//     int i = 0;
//     BIO *bio = NULL;

//     if (constructed & V_ASN1_CONSTRUCTED)
//         p = "cons: ";
//     else
//         p = "prim: ";
//     if (constructed != (V_ASN1_CONSTRUCTED | 1)) {
//         if (BIO_snprintf(str, sizeof(str), "%5ld:d=%-2d hl=%ld l=%4ld %s",
//                          offset, depth, (long)hl, len, p) <= 0)
//             goto err;
//     } else {
//         if (BIO_snprintf(str, sizeof(str), "%5ld:d=%-2d hl=%ld l=inf  %s",
//                          offset, depth, (long)hl, p) <= 0)
//             goto err;
//     }

//     if (bp != NULL) {
//         if (BIO_set_prefix(bp, str) <= 0) {
//             if ((bio = BIO_new(BIO_f_prefix())) == NULL
//                     || (bp = BIO_push(bio, bp)) == NULL)
//                 goto err;
//             pop_f_prefix = 1;
//         }
//         saved_indent = BIO_get_indent(bp);
//         if (BIO_set_prefix(bp, str) <= 0 || BIO_set_indent(bp, indent) <= 0)
//             goto err;
//     }

//     /*
//      * BIO_set_prefix made a copy of |str|, so we can safely use it for
//      * something else, ASN.1 tag printout.
//      */
//     p = str;
//     if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
//         BIO_snprintf(str, sizeof(str), "priv [ %d ] ", tag);
//     else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
//         BIO_snprintf(str, sizeof(str), "cont [ %d ]", tag);
//     else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
//         BIO_snprintf(str, sizeof(str), "appl [ %d ]", tag);
//     else if (tag > 30)
//         BIO_snprintf(str, sizeof(str), "<ASN1 %d>", tag);
//     else
//         p = ASN1_tag2str(tag);

//     i = (BIO_printf(bp, "%-18s", p) > 0);
//  err:
//     if (saved_indent >= 0)
//         BIO_set_indent(bp, saved_indent);
//     if (pop_f_prefix)
//         BIO_pop(bp);
//     BIO_free(bio);
//     return i;
// }


/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <app_errno.h>
#include <stdint.h>
#include <stddef.h>
#include "bsl_asn1.h"
#include "bsl_uio.h"
#include "app_print.h"
static int print_hex(BSL_UIO *outUio, const uint8_t *buf, uint32_t len);
int32_t Asn1ParseInfo(BSL_UIO *outUio, uint32_t offset, uint32_t depth, uint32_t hl, uint32_t len,
                           const char* p, const char* tname);

static int print_int(BSL_UIO *outUio, uint8_t *buff, uint32_t len);
static int print_bool(BSL_UIO *outUio, uint8_t bol);
static int print_obstring(BSL_UIO *outUio, uint8_t *buff, uint32_t len, uint32_t indent);
static int print_oid(BSL_UIO *outUio, uint8_t *buff, uint32_t len);
static int print_time(BSL_UIO *outUio, uint8_t *buff, uint32_t len);
static void asn_print_error_show(int ret);
static int asn_print_fsname(BSL_UIO *out, int indent,const char *fname, const char* sname,
                             const uint8_t flag);

int32_t Asn1ParseFormat(BSL_UIO *outUio, BSL_ASN1_Buffer *buff, const char* fname,uint32_t dlimit, uint32_t indent, uint32_t dump)
{
    uint8_t *data = buff->buff;
    uint32_t len = buff->len > dlimit ? dlimit : buff->len;
    uint8_t tag = buff->tag;
    int ret = HITLS_APP_SUCCESS;

    if (fname) {
        asn_print_fsname(outUio, indent, fname, NULL, 0);
    }

    switch(tag){
        case BSL_ASN1_TAG_NULL:
            return AppPrint(outUio,"NULL",indent);
            break;
        case BSL_ASN1_TAG_BOOLEAN:   
            uint8_t bol=data[0];
            ret =print_bool(outUio, bol);
            break;
        case BSL_ASN1_TAG_ENUMERATED:
            ret=print_int(outUio,data,len);
            break;
        case BSL_ASN1_TAG_INTEGER:
            ret=print_int(outUio,data,len);
            break;
        case BSL_ASN1_TAG_OCTETSTRING:
            if (dump) {
                ret=print_hex(outUio, data, len);
            }
            else {
                ret=print_obstring(outUio, data, len, 6);
            }
            break;
            if (dump) {
                ret=print_hex(outUio, data, len);
            }
            else {
                ret=print_obstring(outUio, data, len, 6);
            }
            break;
        case BSL_ASN1_TAG_OBJECT_DESCP:
            ret =print_oid(outUio, data, len);
            break;
        case BSL_ASN1_TAG_OBJECT_ID:
            ret =print_oid(outUio, data, len);
            break;
        case BSL_ASN1_TAG_REAL:
            AppPrint(outUio, "<REAL unsupported>",indent);
            break;
        case BSL_ASN1_TAG_EMBEDDED_PDV:
            AppPrint(outUio, "<EMBEDDED PDV unsupported>",indent);
            break;
        case BSL_ASN1_TAG_INSTANCE_OF:
            AppPrint(outUio, "<INSTANCE OF unsupported>",indent);
            break;
        case BSL_ASN1_TAG_UTF8STRING:
            ret=print_obstring(outUio,data,len,indent);
            break;
        case BSL_ASN1_TAG_RALATIVE_ID:
            break;
        case BSL_ASN1_TAG_TIME:
            if (dump) {
                if (len <= 32)
                    ret=print_hex(outUio, data, len);
                else
                    ret=print_obstring(outUio, data, len,6);
            }
            else {
                ret=print_time(outUio, data, len);
            }
            break;
        case BSL_ASN1_TAG_SEQUENCE:
            break;
        case BSL_ASN1_TAG_SET:
            break;
        case BSL_ASN1_TAG_PRINTABLESTRING:
            break;
        case BSL_ASN1_TAG_T61STRING:
            break;
        case BSL_ASN1_TAG_IA5STRING:
            if (dump) {
                ret=print_hex(outUio, data, len);
            }
            else {
                ret=print_obstring(outUio, data, len, 6);
            }
            break;
        case BSL_ASN1_TAG_UTCTIME:
            if (dump) {
                ret=print_hex(outUio, data, len);
            }
            else {
                ret=print_time(outUio, data, len);
            }
            break;  
        case BSL_ASN1_TAG_GENERALIZEDTIME:
            break;
        case BSL_ASN1_TAG_BMPSTRING:
            break;  
        default:
            AppPrintError("Unknown tag: %d\n", tag);
    }

    return ret;
}

static int print_hex(BSL_UIO *outUio, const uint8_t *buf, uint32_t len) {
    int ret = HITLS_APP_SUCCESS;
    ret = AppPrint(outUio, ":0x");
    if (ret) {   
        asn_print_error_show(ret);
        return ret;
    }

    for (uint32_t i = 0; i < len; ++i) {
        ret = AppPrint(outUio, "%02X", buf[i]);
        if (ret) {   
            asn_print_error_show(ret);
            return ret;
        }
    }
    return HITLS_APP_SUCCESS;
}

static int print_int(BSL_UIO *outUio,uint8_t *buff,uint32_t len){
    const uint8_t *p = (const uint8_t *)buff;
    uint32_t n = len;
    return print_hex(outUio, p, n);
}
static int print_bool(BSL_UIO *outUio,uint8_t bol){
    const char* str;
    int ret = HITLS_APP_SUCCESS;
    str = bol?":TRUE":":FALSE";

    ret = AppPrint(outUio,str);
    if (ret) {   
        asn_print_error_show(ret);
        return ret;
    }

    return HITLS_APP_SUCCESS;
}
static int print_obstring(BSL_UIO *outUio,uint8_t *buff,uint32_t len, uint32_t indent){
    const uint8_t *p = (const uint8_t *)buff;
    uint32_t n = len;
    const uint32_t per_line = 16;
    int ret = HITLS_APP_SUCCESS;

    for (uint32_t i = 0; i < n; i += per_line) {
        uint32_t chunk = (n - i > per_line) ? per_line : (n - i);

        AppPrint(outUio, "\n%*s%04x - ", (int)indent, "", i);
        // 检查打印结果并处理错误   正常打印的ret为0
        if (ret) {   
            asn_print_error_show(ret);
            return ret;
        }   

        for (uint32_t j = 0; j < chunk; ++j) {
            ret = AppPrint(outUio, "%02x ", p[i + j]);
            // 检查打印结果并处理错误   正常打印的ret为0
            if (ret) {   
                asn_print_error_show(ret);
                return ret;
            }   
        }

    }

    AppPrint(outUio, "\n");
    return HITLS_APP_SUCCESS;
}

static int print_oid(BSL_UIO *outUio,uint8_t *buff, uint32_t len){
    const uint8_t *p = (const uint8_t *)buff;
    uint32_t n = len;
    uint32_t i = 0;
    unsigned first = p[i++];
    unsigned x = first / 40;
    unsigned y = first % 40;
    int ret = HITLS_APP_SUCCESS;

    if (x > 2) { x = 2; y = first - 80; }
    ret = AppPrint(outUio, ":%u.%u", x, y);
    if (ret) {   
        asn_print_error_show(ret);
        return ret;
    }
    while (i < n) {
        uint64_t v = 0;
        int cont = 1;
        do {
            uint8_t b = p[i++];
            v = (v << 7) | (b & 0x7Fu);
            cont = (b & 0x80u) != 0;
            if (i > n) return 0; 
        } while (cont);
        ret = AppPrint(outUio, ".%llu", (unsigned long long)v);
        if (ret) {   
            asn_print_error_show(ret);
            return ret;
        }
    }
    return HITLS_APP_SUCCESS;
}

static int print_time(BSL_UIO *outUio, uint8_t *buff, uint32_t len) {
    /*250923165708Z*/
    /*Sep 23 16:57:08 2025 GMT*/
    const char **months[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    int ret = HITLS_APP_SUCCESS;

    AppPrint(outUio, "%c%c%c %c%c %c%c:%c%c:%c%c 20%c%c GMT",
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][0],
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][1],
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][2],
             buff[4], buff[5],
             buff[6], buff[7],
             buff[8], buff[9],
             buff[10], buff[11],
             buff[0], buff[1]);

    if (ret) {   
        asn_print_error_show(ret);
        return ret;
    }

    return ret;
}


int32_t Asn1ParseInfo(BSL_UIO *outUio, uint32_t offset, uint32_t depth, uint32_t hl, uint32_t len,
                           const char* p, const char* tname){

    int ret = HITLS_APP_SUCCESS; 
    char *chr;

    // 有些上层逻辑根本不需要真正的输出，故设置bp == NULL → 不做打印相关的工作，bp != NULL → 正常设置格式和写数据
    if (outUio == NULL){
        // 设置格式基本信息
        chr = snprintf(chr, sizeof(chr), "%5ld:d=%-2d hl=%ld l=%4ld %s: ", offset, depth, hl, len, p);
        
        // 打印类型名称（可能会受到某些参数影响，先单独列出来了）+ 基本信息
        ret = AppPrint(outUio, "%s%*s%-*s", chr, depth, "", 18-depth, tname); 

        // 检查打印结果并处理错误   正常打印的ret为0
        if (ret) {   
            asn_print_error_show(ret);
            return ret;
        }
    }
    return ret;
}


static int asn_print_fsname(BSL_UIO *out, int indent,
                             const char *fname, const char* sname,
                             const uint8_t flag){

    static const char spaces[] = "                    ";
    static const int nspaces = sizeof(spaces) - 1;
    int ret = HITLS_APP_SUCCESS;
    

    while (indent > nspaces) {
        if (AppPrint(out, spaces) != nspaces)
            return 0;
        indent -= nspaces;
    }
    if (AppPrint(out, "%*s", indent, "") != indent)
        return 0;

    // if (flag & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
    //     sname = NULL;
    // if (flag & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
    //     fname = NULL;

    if (!sname && !fname)
        return ret;


    if (fname) {
        // 打印类型名称（可能会受到某些参数影响，先单独列出来了）+ 基本信息
        ret = AppPrint(out, "%*s%s", indent, "", fname); 

        // 检查打印结果并处理错误   正常打印的ret为0
        if (ret) {   
            asn_print_error_show(ret);
            return ret;
        }
    }           

    return ret;
}


static void asn_print_error_show(int ret){
    switch (ret) {
        case HITLS_APP_INVALID_ARG:
            printf("Invalid argument passed to AppPrint\n");
            break;
        case HITLS_APP_MEM_ALLOC_FAIL:
            printf("Memory allocation failed\n");
            break;
        case HITLS_APP_SECUREC_FAIL:
            printf("Formatting failed\n");
            break;
        default:
            printf("Unknown error occurred, error code: %d\n", ret);
    }
}